#!/usr/bin/env python2

import os
import re
import sys
import json
import time
import mmap
import errno
import shelve
import socket
import shutil
import threading
import etcd
import ConfigParser

from config import Config
from optparse import OptionParser
from pool_manage import PoolManage
from utils import Exp, _dmsg, _dwarn, _derror, _human_readable, _human_unreadable, _exec_pipe, _exec_pipe1, _exec_system, \
        _str2dict, _syserror, _syswarn

INTERVAL = 180
CAPACITY = 10
CONTINUITY = 5
WORKDIR = "/dev/shm/lich4/poolstatus-tmp/"

class PoolStatusTool(PoolManage):
    def __init__(self):
        config = Config()
        super(PoolStatusTool, self).__init__(config)
        self.etcdclt = etcd.Client(port=2379)

        if not os.path.exists(WORKDIR):
            os.makedirs(WORKDIR)

    def load(self, pool):
        path = os.path.join(WORKDIR, pool)
        s = shelve.open(path, writeback=True)
        if not s.has_key('pool'):
            s['pool'] = pool
        if not s.has_key('record'):
            s['record'] = []
        if not s.has_key('last'):
            s['last'] = time.time()

        if time.time() - s['last'] > INTERVAL:
            _dwarn("record timeout, stash them!")
            s['record'] = []

        return s

    def update(self, pool_shelve, timeout):
        pool = pool_shelve['pool']
        record = pool_shelve['record']
        record.append(self.status(pool, timeout))
        pool_shelve['last'] = time.time()
        if (len(record) > CAPACITY):
            surplus = len(record) - CAPACITY
            pool_shelve['record'] = record[surplus:]

    def analysis(self, timeout, debug=False):
        poolstatus = {}
        threads = []
        poolist = self.pool_list()

        def __analysis(pool, poolstatus, timeout):
            pool_shelve = self.load(pool)
            self.update(pool_shelve, timeout)
            record = pool_shelve['record']
            valid = [r for r in reversed(record)][:CONTINUITY]

            if len(valid) < CONTINUITY:
                poolstatus[pool] = "Unknow"
            elif len(set(valid)) == 1:
                poolstatus[pool] = valid[-1]
            else:
                poolstatus[pool] = "Unknow"

        for pool in poolist:
            threads.append(threading.Thread(target=__analysis, args=(pool, poolstatus, timeout)))

        for t in threads:
            t.start()

        for t in threads:
            t.join()

        return poolstatus

    def is_admin(self):
        hostname = self.config.hostname
        return True if hostname == self.etcdclt.get('/lich4/misc/master').value else False

    def etcd_get(self, pool):
        path = os.path.join("/lich4/status", pool)
        try:
            value = self.etcdclt.get(path).value
        except etcd.EtcdKeyNotFound:
            return None
        return value

    def etcd_set(self, pool, status, ttl=10):
        path = os.path.join("/lich4/status", pool)
        self.etcdclt.write(path, status, ttl)
        _dmsg("set %s %s ttl %d"%(pool, status, ttl))

if __name__ == '__main__':
    parser = OptionParser()
    parser.add_option("-s", "--show", action='store_true', default=False,
            dest="show", help="show")
    parser.add_option("-d", "--debug", action='store_true', default=False,
            dest="debug", help="debug")
    parser.add_option("-t", "--ttl", nargs=1, type='int', default=5,
            dest="ttl", help="ttl")
    parser.add_option("-o", "--timeout", nargs=1, type='int', default=20,
            dest="timeout", help="timeout")
    parser.add_option("-a", "--analysis", action='store_true', default=False,
            dest="analysis", help="analysis")

    (options, args) = parser.parse_args()
    if not options.show and not options.analysis:
        sys.exit(errno.EINVAL)

    def analysis(ttl=5, timeout=20):
        ps = PoolStatusTool()

        if not ps.is_admin():
            sys.exit(errno.EPERM)

        poolstatus =  ps.analysis(timeout)
        for pool, status in poolstatus.items():

            if status == "Unknow":
                laststaus =ps.etcd_get(pool)
                status = laststaus if laststaus else 'Pendding'
                _dwarn("pool %s laststaus %s -> update %s" %(pool, laststaus, status))
            ps.etcd_set(pool, status, ttl)

    def show():
        ps = PoolStatusTool()

        poolist = ps.pool_list()
        for pool in poolist:
            print "pool %s, recode %s" % (pool, str(ps.load(pool)))

    if options.analysis:
        analysis(options.ttl, options.timeout)
    elif options.show:
        show()
